home *** CD-ROM | disk | FTP | other *** search
/ Dr. Windows 3 / dr win3.zip / dr win3 / NEW_TECH / TUSRC.ZIP / SRC / SPLIT.C < prev    next >
C/C++ Source or Header  |  1993-10-02  |  13KB  |  557 lines

  1. /* split.c -- split a file into pieces.
  2.    Copyright (C) 1988, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* By tege@sics.se, with rms.
  19.  
  20.    To do:
  21.    * Implement -t CHAR or -t REGEX to specify break characters other
  22.      than newline. */
  23.  
  24. #include <stdio.h>
  25. #include <io.h>
  26. #include "../lib/getopt.h"
  27. #include <sys/types.h>
  28. #include "system.h"
  29. #include "version.h"
  30.  
  31. char *xmalloc ();
  32. void error ();
  33.  
  34. static int convint ();
  35. static int isdigits ();
  36. static int stdread ();
  37. static void line_bytes_split ();
  38. static void bytes_split ();
  39. static void cwrite ();
  40. static void lines_split ();
  41. static void next_file_name ();
  42.  
  43. /* The name this program was run with. */
  44. char *program_name;
  45.  
  46. /* Base name of output files.  */
  47. static char *outfile;
  48.  
  49. /* Pointer to the end of the prefix in OUTFILE.
  50.    Suffixes are inserted here.  */
  51. static char *outfile_mid;
  52.  
  53. /* Pointer to the end of OUTFILE. */
  54. static char *outfile_end;
  55.  
  56. /* Status for outfile name generation.  */
  57. static unsigned outfile_count = (unsigned) -1;
  58. static unsigned outfile_name_limit = 25 * 26;
  59. static unsigned outfile_name_generation = 1;
  60.  
  61. /* Name of input file.  May be "-".  */
  62. static char *infile;
  63.  
  64. /* Descriptor on which input file is open.  */
  65. static int input_desc;
  66.  
  67. /* Descriptor on which output file is open.  */
  68. static int output_desc;
  69.  
  70. /* If non-zero, display usage information and exit.  */
  71. static int flag_help;
  72.  
  73. /* If non-zero, print the version on standard error.  */
  74. static int flag_version;
  75.  
  76. static struct option const longopts[] =
  77. {
  78.   {"bytes", required_argument, NULL, 'b'},
  79.   {"lines", required_argument, NULL, 'l'},
  80.   {"line-bytes", required_argument, NULL, 'C'},
  81.   {"help", no_argument, &flag_help, 1},
  82.   {"version", no_argument, &flag_version, 1},
  83.   {NULL, 0, NULL, 0}
  84. };
  85.  
  86. static void
  87. usage (reason)
  88.     char *reason;
  89. {
  90.   if (reason != NULL)
  91.     fprintf (stderr, "%s: %s\n", program_name, reason);
  92.   fprintf (stderr, "\
  93. Usage: %s [-lines] [-l lines] [-b bytes[bkm]] [-C bytes[bkm]]\n\
  94.        [--lines=lines] [--bytes=bytes[bkm]] [--line-bytes=bytes[bkm]]\n\
  95.        [--help] [--version] [infile [outfile-prefix]]\n",
  96.        program_name);
  97.   exit (2);
  98. }
  99.  
  100. void
  101. main (argc, argv)
  102.     int argc;
  103.     char *argv[];
  104. {
  105.   struct stat stat_buf;
  106.   int num;            /* numeric argument from command line */
  107.   enum
  108.     {
  109.       type_undef, type_bytes, type_byteslines, type_lines, type_digits
  110.     } split_type = type_undef;
  111.   int in_blk_size;        /* optimal block size of input file device */
  112.   char *buf;            /* file i/o buffer */
  113.   int accum = 0;
  114.   char *outbase;
  115.   int c;
  116.   int digits_optind = 0;
  117.  
  118.   program_name = argv[0];
  119.  
  120.   /* Parse command line options.  */
  121.  
  122.   infile = "-";
  123.   outbase = "x";
  124.  
  125.   while (1)
  126.     {
  127.       /* This is the argv-index of the option we will read next.  */
  128.       int this_optind = optind ? optind : 1;
  129.  
  130.       c = getopt_long (argc, argv, "0123456789b:l:C:", longopts, (int *) 0);
  131.       if (c == EOF)
  132.     break;
  133.  
  134.       switch (c)
  135.     {
  136.     case 0:
  137.       break;
  138.  
  139.     case 'b':
  140.       if (split_type != type_undef)
  141.         usage ("cannot split in more than one way");
  142.       split_type = type_bytes;
  143.       if (convint (optarg, &accum) == -1)
  144.         usage ("invalid number of bytes");
  145.       break;
  146.  
  147.     case 'l':
  148.       if (split_type != type_undef)
  149.         usage ("cannot split in more than one way");
  150.       split_type = type_lines;
  151.       if (!isdigits (optarg))
  152.         usage ("invalid number of lines");
  153.       accum = atoi (optarg);
  154.       break;
  155.  
  156.     case 'C':
  157.       if (split_type != type_undef)
  158.         usage ("cannot split in more than one way");
  159.       split_type = type_byteslines;
  160.       if (convint (optarg, &accum) == -1)
  161.         usage ("invalid number of bytes");
  162.       break;
  163.  
  164.     case '0':
  165.     case '1':
  166.     case '2':
  167.     case '3':
  168.     case '4':
  169.     case '5':
  170.     case '6':
  171.     case '7':
  172.     case '8':
  173.     case '9':
  174.       if (split_type != type_undef && split_type != type_digits)
  175.         usage ("cannot split in more than one way");
  176.       if (digits_optind != 0 && digits_optind != this_optind)
  177.         accum = 0;        /* More than one number given; ignore other. */
  178.       digits_optind = this_optind;
  179.       split_type = type_digits;
  180.       accum = accum * 10 + c - '0';
  181.       break;
  182.  
  183.     default:
  184.       usage ((char *)0);
  185.     }
  186.     }
  187.  
  188.   if (flag_version)
  189.     {
  190.       fprintf (stderr, "%s\n", version_string);
  191.       exit (0);
  192.     }
  193.  
  194.   if (flag_help)
  195.     usage ((char *)0);
  196.  
  197.   /* Handle default case.  */
  198.   if (split_type == type_undef)
  199.     {
  200.       split_type = type_lines;
  201.       accum = 1000;
  202.     }
  203.  
  204.   if (accum < 1)
  205.     usage ("invalid number");
  206.   num = accum;
  207.  
  208.   /* Get out the filename arguments.  */
  209.  
  210.   if (optind < argc)
  211.     infile = argv[optind++];
  212.  
  213.   if (optind < argc)
  214.     outbase = argv[optind++];
  215.  
  216.   if (optind < argc)
  217.     usage ("too many arguments");
  218.  
  219.   /* Open the input file.  */
  220.   if (!strcmp (infile, "-"))
  221.     input_desc = 0;
  222.   else
  223.     {
  224.       input_desc = open (infile, O_RDONLY);
  225.       if (input_desc < 0)
  226.     error (1, errno, "%s", infile);
  227.     }
  228.  
  229.   /* No output file is open now.  */
  230.   output_desc = -1;
  231.  
  232.   /* Copy the output file prefix so we can add suffixes to it.
  233.      26**29 is certainly enough output files!  */
  234.  
  235.   outfile = xmalloc (strlen (outbase) + 30);
  236.   strcpy (outfile, outbase);
  237.   outfile_mid = outfile + strlen (outfile);
  238.   outfile_end = outfile_mid + 2;
  239.   bzero (outfile_mid, 30);
  240.   outfile_mid[0] = 'a';
  241.   outfile_mid[1] = 'a' - 1;  /* first call to next_file_name makes it an 'a' */
  242.  
  243.   /* Get the optimal block size of input device and make a buffer.  */
  244.  
  245.   if (fstat (input_desc, &stat_buf) < 0)
  246.     error (1, errno, "%s", infile);
  247.   in_blk_size = ST_BLKSIZE (stat_buf);
  248.  
  249.   buf = xmalloc (in_blk_size + 1);
  250.  
  251.   switch (split_type)
  252.     {
  253.     case type_digits:
  254.     case type_lines:
  255.       lines_split (num, buf, in_blk_size);
  256.       break;
  257.  
  258.     case type_bytes:
  259.       bytes_split (num, buf, in_blk_size);
  260.       break;
  261.  
  262.     case type_byteslines:
  263.       line_bytes_split (num);
  264.       break;
  265.  
  266.     default:
  267.       abort ();
  268.     }
  269.  
  270.   if (close (input_desc) < 0)
  271.     error (1, errno, "%s", infile);
  272.   if (output_desc >= 0 && close (output_desc) < 0)
  273.     error (1, errno, "%s", outfile);
  274.  
  275.   exit (0);
  276. }
  277.  
  278. /* Return nonzero if the string STR is composed entirely of decimal digits.  */
  279.  
  280. static int
  281. isdigits (str)
  282.     char *str;
  283. {
  284.   do
  285.     {
  286.       if (!ISDIGIT (*str))
  287.     return 0;
  288.       str++;
  289.     }
  290.   while (*str);
  291.   return 1;
  292. }
  293.  
  294. /* Put the value of the number in STR into *VAL.
  295.    STR can specify a positive integer, optionally ending in `k'
  296.    to mean kilo or `m' to mean mega.
  297.    Return 0 if STR is valid, -1 if not. */
  298.  
  299. static int
  300. convint (str, val)
  301.      char *str;
  302.      int *val;
  303. {
  304.   int multiplier = 1;
  305.   int arglen = strlen (str);
  306.  
  307.   if (arglen > 1)
  308.     {
  309.       switch (str[arglen - 1])
  310.     {
  311.     case 'b':
  312.       multiplier = 512;
  313.       str[arglen - 1] = '\0';
  314.       break;
  315.     case 'k':
  316.       multiplier = 1024;
  317.       str[arglen - 1] = '\0';
  318.       break;
  319.     case 'm':
  320.       multiplier = 1048576;
  321.       str[arglen - 1] = '\0';
  322.       break;
  323.     }
  324.     }
  325.   if (!isdigits (str))
  326.     return -1;
  327.   *val = atoi (str) * multiplier;
  328.   return 0;
  329. }
  330.  
  331. /* Split into pieces of exactly NCHARS bytes.
  332.    Use buffer BUF, whose size is BUFSIZE.  */
  333.  
  334. static void
  335. bytes_split (nchars, buf, bufsize)
  336.     int nchars;
  337.     char *buf;
  338.     int bufsize;
  339. {
  340.   int n_read;
  341.   int new_file_flag = 1;
  342.   int to_read;
  343.   int to_write = nchars;
  344.   char *bp_out;
  345.  
  346.   do
  347.     {
  348.       n_r